package cfdt.ldl.web.interceptor;

import cfdt.ldl.core.system.SystemConstants;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cfdt.common.enums.CommonCodeEnum;
import cfdt.common.vo.ObjectBaseResponse;
import cfdt.common.wrapper.RequestWrapper;
import cfdt.common.constants.DefaultConstants;
import cfdt.common.exception.CodeException;
import cfdt.tools.json.JsonUtil;
import cfdt.ldl.web.util.SignUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

import static cfdt.common.constants.DefaultConstants.HTTP_STATUS_AUTH;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

/**
 * <pre>
 * Modify Information:基本拦截器（所有请求均到此处进行拦截）
 * Author       Date          Description
 * ============ ============= ============================
 * chenzhibin      2021年1月20日       create this file
 * </pre>
 */
@Component
public class BaseInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger("BaseInterceptor");


    // 在业务处理器处理请求之前被调用
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 请求参数校验
        String token = request.getHeader(DefaultConstants.ACCESS_TOKEN);
        // 签名密钥，如果有token就使用token当密钥，没token就使用系统密钥
        String checkReqSecretKey = StrUtil.isEmpty(token) ? SystemConstants.secret : token;

        String originSign = request.getHeader(DefaultConstants.SIGN);
        String paramStr = "";
        // 获取请求报文体
        if(request instanceof RequestWrapper){
            paramStr = ((RequestWrapper) request).getBody();
        }

        // 过滤链接不做非法字符校验（过滤富文本的接口）
        String url = request.getRequestURI();
        if (StrUtil.isNotEmpty(paramStr)
                && StrUtil.startWithIgnoreCase(request.getContentType(), APPLICATION_JSON_VALUE)){
            // 校验SQL注入,默认要校验
            boolean checkSql = true;
            if (StrUtil.isNotEmpty(SystemConstants.illegalExceptUrls)) {
                // 忽略校验的URL接口，如富文本
                List<String> illegalExceptUrls = StrUtil.split(SystemConstants.illegalExceptUrls, "\\|");
                if (CollUtil.contains(illegalExceptUrls, url)) {
                    // 是富文本就不校验SQL注入了
                    checkSql = false;
                }
            }

            // 如果需要校验SQL注入
            if (checkSql) {
                try{
                    // 防SQL注入校验
                    SignUtil.check(paramStr, SystemConstants.illegalCharRegex, SystemConstants.illegalSQL);
                } catch (CodeException ce){
                    logger.error("{}请求参数签名验证失败{}", url, ce.getMessage());
                    // 处理失败场景HTTP响应
                    onFail(response, ce);
                    return false;
                }
            }

            // 请求参数签名验证
            if (StrUtil.isNotEmpty(checkReqSecretKey)) {
                // 比对签名
                boolean signFlag = SignUtil.compareSign(originSign, paramStr, checkReqSecretKey);
                if (!signFlag) {
                    logger.error("{}请求验签失败, errorSign:{}", url, originSign);
                    // 处理失败场景HTTP响应
                    onFail(response, new CodeException(CommonCodeEnum.CODE_0008));
                    return false;
                }
            }
        }

        // TODO 获取请求IP，请求IP黑名单校验？后续再做？

        return true;
    }




    // 在业务处理器处理请求完成之后，生成视图之前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // Do nothing

    }

    // 在DispatcherServlet完全处理完请求之后被调用，可用于清理资源
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // Do nothing

    }


    /**
     * 拦截器失败时的操作
     */
    private void onFail(HttpServletResponse response, CodeException codeException) throws IOException {
        response.setStatus(HTTP_STATUS_AUTH);
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);

        ObjectBaseResponse baseResponse = ObjectBaseResponse.error(codeException);
        response.getWriter().write(JsonUtil.toJsonStr(baseResponse));
    }


}
